﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

using System.Drawing.Imaging;

using AForge;
using AForge.Imaging;
using AForge.Imaging.Filters;
using AForge.Video;
using AForge.Video.DirectShow;

using NAudio;
using NAudio.Wave;

namespace ueg
{
    public partial class Form1 : Form
    {
        public FilterInfoCollection usbWebcams;
        public VideoCaptureDevice camSource;

        Bitmap sample;

        public WaveTone tone = new WaveTone(200, 0.1);
        public DirectSoundOut output = new DirectSoundOut();
        public BlockAlignReductionStream stream;

        // Probably should not use frame counter.  Input is too variable.
        int frames = 0;


        public Form1()
        {
            InitializeComponent();
            //ToneTest();
        }

        private void DisplayRow(List<int> input)
        {
            for (int i = 0; i < input.Count; i++)
            {
                if (input[i] > 50)
                {
                    Console.Write(".");
                }
                else
                {
                    Console.Write(" ");
                }
            }
            Console.Write("\n");
        }

        private void PlayRow(List<int> input)
        {
            try
            {
                double frequency = 0;
                double amplitude = 0;

                for (int i = 0; i < input.Count; i++)
                {
                    frequency += i;
                    amplitude += input[i] + 0.0001;
                }

                try
                {
                    tone.frequency = frequency / input.Count;
                    tone.amplitude = amplitude / input.Count;

                    stream = new BlockAlignReductionStream(tone);
                    output.Init(stream);
                    output.Play();
                }
                catch { }
            }
            catch
            {
                return;
            }
        }

        private void PlayPixels(List<List<int>> input)
        {
            int frequency = 0;
            int amplitude = 0;

            for (int x = 0; x < input.Count; x++)
            {
                PlayRow(input[x]);
                System.Threading.Thread.Sleep(70);
            }
            output.Stop();
        }

        private void PixellatedRows(Bitmap inputPixels)
        {
            BitmapData tempImage = inputPixels.LockBits(
                new Rectangle(0, 0, inputPixels.Width, inputPixels.Height),
                ImageLockMode.ReadWrite,
                inputPixels.PixelFormat);

            int size = tempImage.Stride * tempImage.Height;
            byte[] data = new byte[size];

            System.Runtime.InteropServices.Marshal.Copy(tempImage.Scan0, data, 0, size);

            List<List<int>> pixels = new List<List<int>>();
            int stepSize = 8;
            unsafe
            {
                for (int y = 0; y < tempImage.Height; y+=stepSize)
                {
                    List<int> row = new List<int>();
                    for (int x = 0; x < tempImage.Stride; x+=stepSize)
                    {
                        int pos = y * tempImage.Width + x;
                        row.Add(data[pos] + data[pos+1] + data[pos+2]);
                    }
                    pixels.Add(row);
                    //DisplayRow(row);
                }
            }
            PlayPixels(pixels);
            //Console.WriteLine(pixels.Count); - 30
            //Console.WriteLine(pixels[0].Count); - 40
            System.Runtime.InteropServices.Marshal.Copy(data, 0, tempImage.Scan0, data.Length);
            inputPixels.UnlockBits(tempImage);
        }

        private void cam_NewFrame(object sender, NewFrameEventArgs eventArgs)
        {
            Bitmap image = (Bitmap)eventArgs.Frame.Clone();

            Bitmap gray = Grayscale.CommonAlgorithms.BT709.Apply(image);

            DifferenceEdgeDetector difference = new DifferenceEdgeDetector();
            Bitmap edge = difference.Apply(gray);

            //Pixellate pixellation = new Pixellate();
            //Bitmap pixel = pixellation.Apply(edge);

            sample = edge;
            if (frames % 25 == 0)
            {
                PixellatedRows(edge);
                //frames++;
                Console.WriteLine(frames);
            }

            frames++;
            //Console.WriteLine(frames);
            //PixellatedRows(edge);
            pictureBox1.Image = edge;
        }

        private void ToneTest()
        {
            //tone = new WaveTone(1000, 0.1);
            //stream = new BlockAlignReductionStream(tone);
            for (int i = 0; i < 10000; i++)
            {
                tone.frequency = tone.frequency + 1;
                stream = new BlockAlignReductionStream(tone);

                output.Init(stream);
                output.Play();
                Console.WriteLine(i);
            }
        }

        private void startButton_Click(object sender, EventArgs e)
        {
            if (camSource != null)
            {
                camSource.DesiredFrameSize = new Size(320, 240);
                camSource.NewFrame += new NewFrameEventHandler(cam_NewFrame);
                camSource.Start();
            }
            else
            {
                camerasToolStripMenuItem_Click(sender, e);
            }
        }

        private void stopButton_Click(object sender, EventArgs e)
        {
            if (output != null)
            {
                output.Stop();
            }

            if (camSource != null && camSource.IsRunning)
            {
                camSource.Stop();
            }
        }

        private void processButton_Click(object sender, EventArgs e)
        {
            //stopButton_Click(sender, e);
            PixellatedRows((Bitmap)pictureBox1.Image);
            //startButton_Click(sender, e);
        }

        private void camerasToolStripMenuItem_Click(object sender, EventArgs e)
        {
            CamSelector selector = new CamSelector();

            try
            {
                selector.ShowDialog();

                usbWebcams = selector.usbWebcams;
                camSource = selector.camSource;
            }
            catch { }
        }
    }

    public class WaveTone : WaveStream
    {
        public double frequency;
        public double amplitude;
        public double time;

        public WaveTone(double f, double a)
        {
            time = 0;
            frequency = f;
            amplitude = a;
        }

        public override long Position
        {
            get;
            set;
        }

        public override long Length
        {
            get { return long.MaxValue; }
        }

        public override WaveFormat WaveFormat
        {
            get { return new WaveFormat(44100, 16, 1); }
        }

        public override int Read(byte[] buffer, int offset, int count)
        {
            int samples = count / 2;
            for (int i = 0; i < samples; i++)
            {
                double sine = amplitude * Math.Sin(Math.PI * 2 * frequency * time);
                time += 1.0 / 44100;
                short truncated = (short)Math.Round(sine * (Math.Pow(2, 15) - 1));
                buffer[i * 2] = (byte)(truncated & 0x00ff);
                buffer[i * 2 + 1] = (byte)((truncated & 0xff00) >> 8);
            }

            return count;
        }
    }
}
